/*
 * Project: Digital Wristwatch
 * Author: Zak Kemble, contact@zakkemble.co.uk
 * Copyright: (C) 2013 by Zak Kemble
 * License: GNU GPL v3 (see License.txt)
 * Web: http://blog.zakkemble.co.uk/diy-digital-wristwatch/
 */

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <string.h>
#include <stdio.h>
#include "common.h"
#include "time.h"
#include "devices/PCF8563.h"
#include "devices/buzzer.h"
#include "alarm.h"
#include "pwrmgr.h"
#include "watchconfig.h"
#include "millis/millis.h"

#define USE_RTC_AS_TICK_SRC	0

#define HALF_SECOND_TIMER_EN()	(TCC0.CTRLA = 0x06)
#define HALF_SECOND_TIMER_DIS()	(TCC0.CTRLA = 0x00)

#define unitsOnly(data)		(0x0F & data)
#define tens2Dec(data)		(data>>4)

typedef enum
{
	BEEP_0,
	BEEP_1,
	BEEP_2,
	BEEP_3
} beep_state_t;

s_time timeData;
static volatile bool update;
static bool halfSecond;

static byte bcd2dec(byte);
static byte dec2bcd(byte);

void time_init()
{
#if USE_RTC_AS_TICK_SRC
	OSC.RC32KCAL = SP_ReadCalibrationByte(2);
	OSC.CTRL = 	OSC.CTRL | OSC_RC32KEN_bm;//enable 32.768K
	CLK.RTCCTRL = 0x0D;//32.768kHz from 32.768kHz internal oscillator

	RTC.PER = 64;
	RTC.INTCTRL = 0x01;
	RTC.CTRL = 0x06;//div 1024
#else
	//8uS per second slow error, Need sync from RTC period
	TCC0.PER = 31248;//500MS
	TCC0.INTCTRLA = 0x01; //HI Pri
#endif

	time_wake();
}

void time_sleep()
{
	// Set next alarm
	s_alarm* nextAlarm = alarm_getNext();
	if(nextAlarm != NULL)
	{
		s_alarm alarm;
		alarm.min = dec2bcd(nextAlarm->min);
		alarm.hour = dec2bcd(nextAlarm->hour);
		alarm.days = alarm_getNextDay();
		PCF8563_setUserAlarmWake(&alarm);
	}
	else
		PCF8563_setUserAlarmWake(NULL);

	PCF8563_setSystemAlarmWake();

	update = false;

	HALF_SECOND_TIMER_DIS();
	MILLIS_TIMER_DIS();
}

rtcwake_t time_wake()
{
	byte dec;

	MILLIS_TIMER_EN();
	HALF_SECOND_TIMER_EN();

	// Get time data
	PCF8563_get(&timeData);

	// Convert to decimal
	dec	= bcd2dec(timeData.secs);
	if(dec > 59) { dec = 0; }
	timeData.secs = dec;

	dec	= bcd2dec(timeData.mins);
	if(dec > 59) { dec = 0; }
	timeData.mins = dec;

	dec	= bcd2dec(timeData.hours);
	if(dec > 23) { dec = 0; }
	timeData.hours = dec;

	dec	= bcd2dec(timeData.date);
	if((dec > 31) || (dec < 1)) { dec = 1; }
	timeData.date = dec;

	dec	= bcd2dec(timeData.day);
	if(dec > 6) { dec = 0; }
	timeData.day = dec;

	dec	= bcd2dec(timeData.month);
	if((dec > 12) || (dec < 1)) { dec = 1; }
	timeData.month = dec-1;

	dec	= bcd2dec(timeData.year);
	if(dec > 99) { dec = 13; }
	timeData.year = dec;

	update = false;

	// Check alarms
	bool userAlarm = PCF8563_userAlarmState();
	bool systemAlarm = PCF8563_systemAlarmState();

	if(userAlarm && systemAlarm)
		return RTCWAKE_USER_SYSTEM;
	else if(userAlarm)
		return RTCWAKE_USER;
	else if(systemAlarm)
		return RTCWAKE_SYSTEM;

	return RTCWAKE_NONE;
}

bool time_halfSecond()
{
	return halfSecond;
}

void time_set(s_time* timeDataSet)
{
//	TCNT2 = 0x00;

	halfSecond = false;
	memcpy(&timeData, timeDataSet, sizeof(s_time));

	// Convert to BCD
	s_time tmp;
	tmp.secs	= 0;
	tmp.mins	= dec2bcd(timeDataSet->mins);
	tmp.hours	= dec2bcd(timeDataSet->hours);
	tmp.date	= dec2bcd(timeDataSet->date);
	tmp.day		= timeDataSet->day; // Don't need to convert to BCD since it only stores 0 - 6
	tmp.month	= dec2bcd(timeDataSet->month);
	tmp.year	= dec2bcd(timeDataSet->year);

	PCF8563_save(&tmp);

	alarm_updateNextAlarm();
}

bool time_isLeapYear(byte year)
{
	uint fullYear = year + 2000;
	return ((fullYear & 3) == 0 && ((fullYear % 25) != 0 || (fullYear & 15) == 0));
}

byte time_dow(int y, byte m, byte d)
{
	static byte t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
	y -= m < 3;
	byte dow = (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;

	// 0 = Sunday, 1 = Monday, but Monday should be 0
	dow--;
	if(dow == 255) // Overflowed, must have been 0 which is Sunday
		dow = 6;
	return dow;
}

bool time_update()
{
	if(!update)
		return false;

	update = false;

	if(!halfSecond)
		return false;

	// Slightly modified code from AVR134
	if(++timeData.secs == 60)
	{
		timeData.secs = 0;
		if(++timeData.mins == 60)
		{
			timeData.mins = 0;
			if(++timeData.hours == 24)
			{
				timeData.hours = 0;
				if (++timeData.date == 32)
				{
					timeData.month++;
					timeData.date = 1;
				}
				else if (timeData.date == 31)
				{
					if (timeData.month == 3 || timeData.month == 5 || timeData.month == 8 || timeData.month == 10)
					{
						timeData.month++;
						timeData.date = 1;
					}
				}
				else if (timeData.date == 30)
				{
					if(timeData.month == 1)
					{
						timeData.month++;
						timeData.date = 1;
					}
				}
				else if (timeData.date == 29)
				{
					if(timeData.month == 1 && !time_isLeapYear(timeData.year))
					{
						timeData.month++;
						timeData.date = 1;
					}
				}

				if (timeData.month == 13)
				{
					timeData.month = 1;
					timeData.year++;
					if(timeData.year == 100)
						timeData.year = 0;
				}

				if(++timeData.day == 7)
					timeData.day = 0;
			}
		}
	}

	//printf_P(PSTR("%02hhu:%02hhu:%02hhu\n"), timeData.hours, timeData.mins, timeData.secs);

	return true;
}

static byte bcd2dec(byte bcd)
{
	return ((tens2Dec(bcd) * 10) + unitsOnly(bcd));
}

static byte dec2bcd(byte dec)
{
	return (((dec / 10) << 4) + (dec % 10));
}

#if USE_RTC_AS_TICK_SRC
ISR(RTC_OVF_vect)
#else
ISR(TCC0_OVF_vect)
#endif
{
	//DBG_TRIGGER();

	halfSecond = !halfSecond;
	if(halfSecond) {
		update = true;
	}
}

